/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkFile, ts } from "arkanalyzer/lib";
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import Logger, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { Defects, IssueReport } from "../../model/Defects";
import { Rule } from "../../model/Rule";
import { FileMatcher, MatcherCallback, MatcherTypes } from "../../Index";

const logger = Logger.getLogger(
  LOG_MODULE_TYPE.HOMECHECK,
  "NamingConventionCheck"
);

type Selector =
  | "default"
  | "variable"
  | "function"
  | "parameter"
  | "property"
  | "parameterProperty"
  | "method"
  | "accessor"
  | "enumMember"
  | "class"
  | "interface"
  | "typeAlias"
  | "enum"
  | "typeParameter"
  | "memberLike"
  | "typeLike"
  | "variableLike"
  | "classMethod"
  | "objectLiteralMethod"
  | "typeMethod"
  | "classProperty"
  | "objectLiteralProperty"
  | "typeProperty"
  | "import";

type Format =
  | "camelCase"
  | "strictCamelCase"
  | "PascalCase"
  | "StrictPascalCase"
  | "snake_case"
  | "UPPER_CASE";

type Modifier =
  | "abstract"
  | "async"
  | "const"
  | "default"
  | "destructured"
  | "exported"
  | "global"
  | "namespace"
  | "override"
  | "#private"
  | "private"
  | "protected"
  | "public"
  | "readonly"
  | "requiresQuotes"
  | "static"
  | "unused";

type TypeOption = "array" | "boolean" | "function" | "number" | "string";

type Option = {
  selector: Selector | Selector[];
  format?: Format[] | null;
  modifiers?: Modifier[];
  types?: TypeOption[];
  filter?:
    | string
    | {
        regex: string;
        match: boolean;
      };
  custom?: {
    regex: string;
    match: boolean;
  };
  leadingUnderscore?:
    | "forbid"
    | "require"
    | "requireDouble"
    | "allow"
    | "allowDouble"
    | "allowSingleOrDouble";
  trailingUnderscore?:
    | "forbid"
    | "require"
    | "requireDouble"
    | "allow"
    | "allowDouble"
    | "allowSingleOrDouble";
  prefix?: string[];
  suffix?: string[];
};

const defaultOptions: Option[] = [
  {
    selector: "default",
    format: ["camelCase"],
    leadingUnderscore: "allow",
    trailingUnderscore: "allow",
  },
  {
    selector: "import",
    format: ["camelCase", "PascalCase"],
  },
  {
    selector: "variable",
    format: ["camelCase", "UPPER_CASE"],
    leadingUnderscore: "allow",
    trailingUnderscore: "allow",
  },
  {
    selector: "typeLike",
    format: ["PascalCase"],
  },
];

export class NamingConventionCheck implements BaseChecker {
  readonly metaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/naming-convention-check.md",
    description: "Enforce naming conventions for everything across a codebase.",
  };

  public issues: any[] = [];
  public defects: Defects[] = [];
  public rule: Rule;
  private currentArkFile: ArkFile | null = null;
  private typeChecker: ts.TypeChecker;
  private fileMatcher: FileMatcher = {
    matcherType: MatcherTypes.FILE,
  };
  private selectors: string[] = [];
  private formats: Format[] = [];
  private ruleConfigs = {
    prefixes: new Set<string>(),
    suffixes: new Set<string>(),
  };

  private options: Option[];
  private methodArr: ts.Node[] = [];
  private defaultArr: ts.Node[] = [];
  private variableArr: ts.Node[] = [];
  private functionArr: ts.Node[] = [];
  private parameterArr: ts.Node[] = [];
  private parameterPropertyArr: ts.Node[] = [];
  private accessorArr: ts.Node[] = [];
  private enumMemberArr: ts.Node[] = [];
  private classArr: ts.Node[] = [];
  private interfaceArr: ts.Node[] = [];
  private typeAliasArr: ts.Node[] = [];
  private enumArr: ts.Node[] = [];
  private typeParameterArr: ts.Node[] = [];
  private memberLikeArr: ts.Node[] = [];
  private typeLikeArr: ts.Node[] = [];
  private variableLikeArr: ts.Node[] = [];
  private classMethodArr: ts.Node[] = [];
  private objectLiteralMethodArr: ts.Node[] = [];
  private typeMethodArr: ts.Node[] = [];
  private classPropertyArr: ts.Node[] = [];
  private objectLiteralPropertyArr: ts.Node[] = [];
  private typePropertyArr: ts.PropertySignature[] = [];
  private importArr: ts.ImportDeclaration[] = [];
  private propertyArr: ts.Node[] = [];

  constructor(customOption?: Option[]) {
    if (customOption) {
      this.options = customOption;
    }
  }

  public registerMatchers(): MatcherCallback[] {
    const matchFileCb: MatcherCallback = {
      matcher: this.fileMatcher,
      callback: this.check,
    };
    return [matchFileCb];
  }

  public check = (targetField: ArkFile): void => {
    this.currentArkFile = targetField;
    const filePath = targetField.getFilePath();
    const code = targetField.getCode();
    const program = ts.createProgram({
      rootNames: [filePath],
      options: {
        skipDefaultLibCheck: true,
        skipLibCheck: true,
      },
      host: ts.createCompilerHost({}),
    });
    if (program) {
      this.typeChecker = program.getTypeChecker();
    }
    const node = ts.createSourceFile(
      filePath,
      code,
      ts.ScriptTarget.Latest,
      true
    );

    this.options = this.rule.option?.length
      ? (this.rule.option as Option[])
      : defaultOptions;

    this.getNodeArr(node);

    const defaultItems = this.options.filter(
      (item) => item.selector === "default"
    );

    const nonDefaultItems = this.options.filter(
      (item) => item.selector !== "default"
    );

    const result1: Option[] = [...nonDefaultItems, ...defaultItems];

    const defaultItems1 = result1.filter((item) => item.modifiers);

    const nonDefaultItems1 = result1.filter((item) => !item.modifiers);

    const result: Option[] = [...nonDefaultItems1, ...defaultItems1];

    result.forEach((option) => {
      if (Array.isArray(option.selector)) {
        option.selector.forEach((selector) => {
          this.getOptionForSelector(selector, option);
          this.selectors.push(selector);
        });
      } else {
        this.getOptionForSelector(option.selector, option);
        this.selectors.push(option.selector);
      }

      option.format?.forEach((format) => {
        this.formats.push(format);
      });

      option.prefix?.forEach((p) => this.ruleConfigs.prefixes.add(p));

      option.suffix?.forEach((s) => this.ruleConfigs.suffixes.add(s));
    });
  };

  private getNodeArr(node: ts.Node) {
    const visit = (node: ts.Node) => {
      if (ts.isVariableDeclaration(node)) {
        this.variableArr.push(node);
      }
      if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
        this.functionArr.push(node);
      }
      if (ts.isClassDeclaration(node)) {
        this.classArr.push(node);
      }
      if (ts.isInterfaceDeclaration(node)) {
        this.interfaceArr.push(node);
      }
      if (ts.isEnumDeclaration(node)) {
        this.enumArr.push(node);
      }
      if (ts.isMethodDeclaration(node)) {
        this.methodArr.push(node);
      }
      if (ts.isPropertyDeclaration(node)) {
        this.propertyArr.push(node);
      }

      if (ts.isParameter(node)) {
        this.parameterArr.push(node);
      }
      if (ts.isImportDeclaration(node)) {
        this.importArr.push(node);
      }
      if (ts.isParameterPropertyDeclaration(node, node.parent)) {
        this.parameterPropertyArr.push(node);
      }

      if (ts.isGetAccessor(node) || ts.isSetAccessor(node)) {
        this.accessorArr.push(node);
      }
      if (ts.isEnumMember(node)) {
        this.enumMemberArr.push(node);
      }
      if (ts.isTypeAliasDeclaration(node)) {
        this.typeAliasArr.push(node);
      }
      if (ts.isTypeParameterDeclaration(node)) {
        this.typeParameterArr.push(node);
      }
      if (
        ts.isMethodDeclaration(node) ||
        ts.isPropertyDeclaration(node) ||
        ts.isGetAccessor(node) ||
        ts.isSetAccessor(node) ||
        ts.isEnumMember(node)
      ) {
        this.memberLikeArr.push(node);
      }
      if (
        ts.isClassDeclaration(node) ||
        ts.isInterfaceDeclaration(node) ||
        ts.isTypeAliasDeclaration(node) ||
        ts.isEnumDeclaration(node) ||
        ts.isTypeParameterDeclaration(node)
      ) {
        this.typeLikeArr.push(node);
      }
      if (
        ts.isVariableDeclaration(node) ||
        ts.isFunctionDeclaration(node) ||
        ts.isParameter(node) ||
        ts.isParameterPropertyDeclaration(node, node.parent)
      ) {
        this.variableLikeArr.push(node);
      }
      if (ts.isMethodDeclaration(node) && ts.isClassDeclaration(node.parent)) {
        this.classMethodArr.push(node);
      }
      if (
        ts.isMethodDeclaration(node) &&
        ts.isObjectLiteralExpression(node.parent)
      ) {
        this.objectLiteralMethodArr.push(node);
      }
      if (ts.isMethodSignature(node)) {
        this.typeMethodArr.push(node);
      }
      if (
        ts.isPropertyDeclaration(node) &&
        ts.isClassDeclaration(node.parent)
      ) {
        this.classPropertyArr.push(node);
      }
      if (
        ts.isPropertyAssignment(node) &&
        ts.isObjectLiteralExpression(node.parent)
      ) {
        this.objectLiteralPropertyArr.push(node);
      }
      if (ts.isPropertySignature(node)) {
        this.typePropertyArr.push(node);
      }
      if (ts.isIdentifier(node)) {
        this.defaultArr.push(node);
      }
      ts.forEachChild(node, visit);
    };
    visit(node);
  }
  private checkParamMap: Map<string, number> = new Map();
  private getOptionForSelector(selector: Selector, option: Option) {
    const exhaustiveCheck = (s: never) => {
      throw new Error(`未处理的选择器类型: ${s}`);
    };
    switch (selector) {
      case "variable":
        this.variableArr.forEach((node) => {
          this.checkVariable(node as ts.VariableDeclaration, option);
        });
        break;

      case "function":
        this.functionArr.forEach((node) => {
          this.checkFunction(
            node as ts.FunctionDeclaration | ts.FunctionExpression,
            option
          );
        });
        break;

      case "class":
        this.classArr.forEach((node) => {
          this.checkClass(node as ts.ClassDeclaration, option);
        });
        break;

      case "interface":
        this.interfaceArr.forEach((node) => {
          this.checkInterface(node, option);
        });
        break;

      case "enum":
        this.enumArr.forEach((node) => {
          this.checkEnum(node, option);
        });
        break;

      case "parameter":
        this.parameterArr.forEach((node) => {
          this.checkParameter(node, option);
        });
        break;

      case "import":
        this.importArr.forEach((node) => {
          if (node.importClause) {
            const namedBindings = node.importClause.namedBindings;
            if (namedBindings && ts.isNamedImports(namedBindings)) {
              namedBindings.elements.forEach((element) => {
                this.checkImport(element, option);
              });
            }
          }
        });
        break;
      case "parameterProperty":
        this.parameterPropertyArr.forEach((node) => {
          this.checkParameterProperty(node, option);
        });
        break;

      case "accessor":
        this.accessorArr.forEach((node) => {
          this.checkAccessor(node, option);
        });
        break;

      case "enumMember":
        this.enumMemberArr.forEach((node) => {
          this.checkEnumMember(node, option);
        });
        break;

      case "typeAlias":
        this.typeAliasArr.forEach((node) => {
          this.checkTypeAlias(node, option);
        });
        break;

      case "typeParameter":
        this.typeParameterArr.forEach((node) => {
          this.checkTypeParameter(node, option);
        });
        break;

      case "classMethod":
        this.classMethodArr.forEach((node) => {
          this.checkClassMethod(node, option);
        });
        break;

      case "objectLiteralMethod":
        this.objectLiteralMethodArr.forEach((node) => {
          this.checkObjectLiteralMethod(node, option);
        });
        break;

      case "typeMethod":
        this.typeMethodArr.forEach((node) => {
          this.checkTypeMethod(node, option);
        });
        break;

      case "classProperty":
        this.classPropertyArr.forEach((node) => {
          this.checkClassProperty(node, option);
        });
        break;

      case "objectLiteralProperty":
        this.objectLiteralPropertyArr.forEach((node) => {
          this.checkObjectLiteralProperty(node, option);
        });
        break;

      case "typeProperty":
        this.typePropertyArr.forEach((node) => {
          this.checkTypeProperty(node, option);
        });
        break;
      case "memberLike":
        this.memberLikeArr.forEach((node) => {
          this.checkMemberLike(node, option);
        });
        break;
      case "method":
        this.methodArr.forEach((node) => {
          this.checkMethod(node as ts.MethodDeclaration, option);
        });
        break;
      case "property":
        this.propertyArr.forEach((node) => {
          this.checkProperty(node, option);
        });
        break;
      case "typeLike":
        this.typeLikeArr.forEach((node) => {
          this.checkTypeLike(node, option);
        });
        break;
      case "variableLike":
        this.variableLikeArr.forEach((node) => {
          this.checkVariableLike(node, option);
        });
        break;
      case "default":
        this.defaultArr.forEach((node) => {
          this.checkDefault(node as ts.Identifier, option);
        });
        break;

      default:
        exhaustiveCheck(selector);
    }
  }

  private checkDefault(node: ts.Identifier, option: Option): void {
    let kind = this.getIdentifierKind(node);
    this.checkNaming(node, node.text, option, kind);
    const [line, character] = this.getNodePosition(node);
    this.checkParamMap.set(node.text, line);
  }

  private getIdentifierKind(node: ts.Identifier): string {
    const parent = node.parent;
    
    if (!parent) {
        return "Default";
    }

    if (ts.isClassDeclaration(parent)) {
        return "Class";
    }

    if (ts.isMethodDeclaration(parent)) {
        if (ts.isClassDeclaration(parent.parent)) {
            return "Class Method";
        }
        return "Method";
    }

    if (ts.isFunctionDeclaration(parent)) {
        return "Function";
    }

    if (ts.isParameter(parent)) {
        if (ts.isMethodDeclaration(parent.parent)) {
            return "Method Parameter";
        }
        if (ts.isFunctionDeclaration(parent.parent)) {
            return "Function Parameter";
        }
        return "Parameter";
    }

    if (ts.isPropertyDeclaration(parent)) {
        return "Class Property";
    }

    if (ts.isVariableDeclaration(parent)) {
        return "Variable";
    }

    if (ts.isInterfaceDeclaration(parent)) {
        return "Interface";
    }

    if (ts.isTypeAliasDeclaration(parent)) {
        return "Type Alias";
    }

    if (ts.isEnumDeclaration(parent)) {
        return "Enum";
    }

    if (ts.isEnumMember(parent)) {
        return "Enum Member";
    }

    if (ts.isImportSpecifier(parent) || ts.isImportClause(parent)) {
        return "Import";
    }

    if (ts.isExportSpecifier(parent)) {
        return "Export";
    }

    if (ts.isGetAccessor(parent)) {
        return "Getter";
    }
    if (ts.isSetAccessor(parent)) {
        return "Setter";
    }

    if (ts.isTypeParameterDeclaration(parent)) {
        return "Type Parameter";
    }

    return "Default";
  }

  private checkVariable(node: ts.VariableDeclaration, option: Option): void {
    if (ts.isIdentifier(node.name)) {
      this.checkNaming(node, node.name.text, option, "Variable");
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private checkFunction(
    node: ts.FunctionDeclaration | ts.FunctionExpression,
    option: Option
  ): void {
    if (node.name && ts.isIdentifier(node.name)) {
      this.checkNaming(node, node.name.text, option, "Function");
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private checkParameter(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "Parameter");
    const [line, character] = this.getNodePosition(node);
    this.checkParamMap.set(node.getText(), line);
  }

  private checkProperty(node: ts.Node, option: Option): void {
    const kind = this.getPropertyKind(node);
    const name = ts.isIdentifier(node) ? node.text : node.getText();
    this.checkNaming(node, name, option, kind);
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(name, line);
    }
  }

  private getPropertyKind(node: ts.Node): string {
    if (ts.isPropertyDeclaration(node) && ts.isClassDeclaration(node.parent)) {
        return "Class Property";
    }
    
    if (ts.isPropertyAssignment(node) && ts.isObjectLiteralExpression(node.parent)) {
        return "Object Literal Property";
    }
    
    if (ts.isPropertySignature(node)) {
        return "Type Property";
    }
    
    return "Property";
  }

  private checkParameterProperty(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "ParameterProperty");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkMethod(node: ts.MethodDeclaration, option: Option): void {
    if (ts.isIdentifier(node.name)) {
      const kind = this.getMethodKind(node);
      this.checkNaming(node, node.name.text, option, kind);
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private getMethodKind(node: ts.MethodDeclaration): string {
    if (ts.isClassDeclaration(node.parent)) {
        return "Class Method";
    }
    
    if (ts.isObjectLiteralExpression(node.parent)) {
        return "Object Literal Method";
    }
    
    if (ts.isInterfaceDeclaration(node.parent) || ts.isTypeLiteralNode(node.parent)) {
        return "Type Method";
    }
    
    return "Method";
  }

  private checkAccessor(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "Accessor");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkEnumMember(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "EnumMember");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkClass(node: ts.ClassDeclaration, option: Option): void {
    if (node.name) {
      this.checkNaming(node, node.name.text, option, "Class");
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(node.name.text, line);
      }
    }
  }

  private checkInterface(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "Interface");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeAlias(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "TypeAlias");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkEnum(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "Enum");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeParameter(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "TypeParameter");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkMemberLike(node: ts.Node, option: Option): void {
    let kind = this.getMemberLikeKind(node);
    let name = this.getMemberName(node);
    
    if (name) {
        this.checkNaming(node, name, option, kind);
        if (!option.prefix && !option.suffix) {
            const [line, character] = this.getNodePosition(node);
            this.checkParamMap.set(name, line);
        }
    }
  }

  private getMemberLikeKind(node: ts.Node): string {
    if (ts.isGetAccessor(node)) {
        return "Accessor";
    }
    if (ts.isSetAccessor(node)) {
        return "Accessor";
    }

    if (ts.isEnumMember(node)) {
        return "Enum Member";
    }

    if (ts.isMethodDeclaration(node)) {
        if (ts.isClassDeclaration(node.parent)) {
            return "Class Method";
        }
        if (ts.isObjectLiteralExpression(node.parent)) {
            return "Object Literal Method";
        }
        return "Method";
    }

    if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent)) {
        return "Parameter Property";
    }

    if (ts.isPropertyDeclaration(node)) {
        if (ts.isClassDeclaration(node.parent)) {
            return "Class Property";
        }
        return "Property";
    }
    if (ts.isPropertyAssignment(node)) {
        return "Object Literal Property";
    }
    if (ts.isPropertySignature(node)) {
        return "Type Property";
    }

    return "MemberLike";
  }

  private getMemberName(node: ts.Node): string | undefined {
    if (ts.isIdentifier(node)) {
        return node.text;
    }
    
    if ('name' in node) {
        const name = (node as any).name;
        if (ts.isIdentifier(name)) {
            return name.text;
        }
        if (ts.isPrivateIdentifier(name)) {
            return name.text;
        }
    }
    
    return node.getText();
  }

  private checkTypeLike(node: ts.Node, option: Option): void {
    const kind = this.getTypeLikeKind(node);
    let name = this.getTypeLikeName(node);
    
    if (name) {
        this.checkNaming(node, name, option, kind);
        if (!option.prefix && !option.suffix) {
            const [line, character] = this.getNodePosition(node);
            this.checkParamMap.set(name, line);
        }
    }
  }

  private getTypeLikeKind(node: ts.Node): string {
    if (ts.isClassDeclaration(node)) {
        return "Class";
    }
    
    if (ts.isEnumDeclaration(node)) {
        return "Enum";
    }
    
    if (ts.isInterfaceDeclaration(node)) {
        return "Interface";
    }
    
    if (ts.isTypeAliasDeclaration(node)) {
        return "Type Alias";
    }
    
    if (ts.isTypeParameterDeclaration(node)) {
        return "Type Parameter";
    }
    
    return "TypeLike";
  }

  private getTypeLikeName(node: ts.Node): string | undefined {
    if (ts.isClassDeclaration(node) || 
        ts.isInterfaceDeclaration(node) || 
        ts.isTypeAliasDeclaration(node) || 
        ts.isEnumDeclaration(node)) {
        return node.name?.text;
    }
    
    if (ts.isTypeParameterDeclaration(node)) {
        return node.name.text;
    }
    
    return node.getText();
  }

  private checkVariableLike(node: ts.Node, option: Option): void {
    const kind = this.getVariableLikeKind(node);
    const name = ts.isIdentifier(node) ? node.text : node.getText();
    
    this.checkNaming(node, name, option, kind);
    if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(name, line);
    }
  }

  private getVariableLikeKind(node: ts.Node): string {
    if (ts.isFunctionDeclaration(node)) {
        return "Function";
    }
    
    if (ts.isParameter(node)) {
        if (ts.isMethodDeclaration(node.parent)) {
            return "Method Parameter";
        }
        if (ts.isFunctionDeclaration(node.parent)) {
            return "Function Parameter";
        }
        return "Parameter";
    }
    
    if (ts.isVariableDeclaration(node)) {
        return "Variable";
    }
    
    return "VariableLike";
  }

  private checkClassMethod(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "ClassMethod");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkObjectLiteralMethod(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "ObjectLiteralMethod");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeMethod(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "TypeMethod");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkClassProperty(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "ClassProperty");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkObjectLiteralProperty(node: ts.Node, option: Option): void {
    this.checkNaming(node, node.getText(), option, "ObjectLiteralProperty");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkTypeProperty(node: ts.PropertySignature, option: Option): void {
    this.checkNaming(node, node.name.getText(), option, "TypeProperty");
    if (!option.prefix && !option.suffix) {
      const [line, character] = this.getNodePosition(node);
      this.checkParamMap.set(node.getText(), line);
    }
  }

  private checkImport(node: ts.Node, option: Option): void {
    if (ts.isImportSpecifier(node)) {
      const importName = node.name.text;
      this.checkNaming(node, importName, option, "Import");
      if (!option.prefix && !option.suffix) {
        const [line, character] = this.getNodePosition(node);
        this.checkParamMap.set(importName, line);
      }
    }
  }

  private checkModifiers(node: ts.Node, option: Option): boolean {
    if (!option.modifiers || option.modifiers.length === 0) {
      return true;
    }

    const nodeModifiers = ts.canHaveModifiers(node)
      ? ts.getModifiers(node)
      : undefined;
    const modifierKinds = new Set(
      nodeModifiers?.map((modifier) => modifier.kind)
    );

    return option.modifiers.every((modifier) => {
      switch (modifier) {
        case "abstract":
          return modifierKinds.has(ts.SyntaxKind.AbstractKeyword);
        case "async":
          return modifierKinds.has(ts.SyntaxKind.AsyncKeyword);
        case "const":
          return (
            ts.isVariableDeclaration(node) &&
            node.parent &&
            ts.isVariableDeclarationList(node.parent) &&
            !!(node.parent.flags & ts.NodeFlags.Const)
          );
        case "exported":
          return modifierKinds.has(ts.SyntaxKind.ExportKeyword);
        case "global":
          return !node.parent || ts.isSourceFile(node.parent);
        case "#private":
          return ts.isPrivateIdentifier(node);
        case "private":
          return modifierKinds.has(ts.SyntaxKind.PrivateKeyword);
        case "protected":
          return modifierKinds.has(ts.SyntaxKind.ProtectedKeyword);
        case "public":
          return (
            modifierKinds.has(ts.SyntaxKind.PublicKeyword) ||
            !(
              modifierKinds.has(ts.SyntaxKind.PrivateKeyword) ||
              modifierKinds.has(ts.SyntaxKind.ProtectedKeyword)
            )
          );
        case "readonly":
          return modifierKinds.has(ts.SyntaxKind.ReadonlyKeyword);
        case "static":
          return modifierKinds.has(ts.SyntaxKind.StaticKeyword);
        case "unused":
          if (ts.isParameter(node) || ts.isVariableDeclaration(node)) {
            const symbol = this.typeChecker.getSymbolAtLocation(node.name);
            if (!symbol) return true;
            const usages = symbol.declarations?.length || 0;
            return usages <= 1;
          }
          return true;
        default:
          return true;
      }
    });
  }

  private checkTypes(node: ts.Node, option: Option): boolean {
    // 如果没有配置 types，直接返回 true
    if (!option.types || option.types.length === 0) {
      return true;
    }

    const type = this.typeChecker.getTypeAtLocation(node);
    if (!type) return false;

    return option.types.some((typeOption) => {
      switch (typeOption) {
        case "array":
          return this.isArrayType(type);
        case "boolean":
          return this.isBooleanType(type);
        case "function":
          return this.isFunctionType(type);
        case "number":
          return this.isNumberType(type);
        case "string":
          return this.isStringType(type);
        default:
          return false;
      }
    });
  }

  private isArrayType(type: ts.Type): boolean {
    return (
      type.symbol?.name === "Array" ||
      (type.flags & ts.TypeFlags.Null) !== 0 ||
      (type.flags & ts.TypeFlags.Undefined) !== 0
    );
  }

  private isBooleanType(type: ts.Type): boolean {
    return (
      (type.flags & ts.TypeFlags.Boolean) !== 0 ||
      (type.flags & ts.TypeFlags.Null) !== 0 ||
      (type.flags & ts.TypeFlags.Undefined) !== 0
    );
  }

  private isFunctionType(type: ts.Type): boolean {
    return (
      type.getCallSignatures().length > 0 ||
      (type.flags & ts.TypeFlags.Null) !== 0 ||
      (type.flags & ts.TypeFlags.Undefined) !== 0
    );
  }

  private isNumberType(type: ts.Type): boolean {
    return (
      (type.flags & ts.TypeFlags.Number) !== 0 ||
      (type.flags & ts.TypeFlags.Null) !== 0 ||
      (type.flags & ts.TypeFlags.Undefined) !== 0
    );
  }

  private isStringType(type: ts.Type): boolean {
    return (
      (type.flags & ts.TypeFlags.String) !== 0 ||
      (type.flags & ts.TypeFlags.Null) !== 0 ||
      (type.flags & ts.TypeFlags.Undefined) !== 0
    );
  }

  private checkNaming(
    node: ts.Node,
    name: string,
    config: Option,
    kind: string
  ) {
    const line = this.getNodePosition(node)[0];
    const character = node.getText();
    const violations: string[] = [];
    let isChecked = false;
    let isCopy = this.selectors.some(
      (item, index) => this.selectors.indexOf(item) !== index
    );
    this.checkParamMap.forEach((value, key) => {
      if (key === character && value === line) {
        isChecked = true;
      }
    });

    if (
      !isChecked &&
      config.types &&
      config.types.length > 0 &&
      !this.checkTypes(node, config)
    ) {
      isChecked = true;
    }

    // 检查修饰符
    if (
      config.modifiers &&
      config.modifiers.length > 0 &&
      !this.checkModifiers(node, config)
    ) {
      isChecked = true;
    }

    // 2. 检查custom正则
    if (config.custom && !isChecked) {
      try {
        const customRegex = new RegExp(config.custom.regex);
        const matchesCustom = customRegex.test(name);
        if (config.custom.match) {
          if (!matchesCustom) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Must match  custom pattern: ${config.custom.regex}`
            );
          }
        } else {
          if (matchesCustom) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Must not match custom pattern: ${config.custom.regex}`
            );
          }
        }
      } catch (e) {
        violations.push(
          `${kind} name "${name}" Invalid custom regex pattern: ${config.custom.regex}`
        );
      }
    }

    // 3. 检查filter
    if (config.filter && !isChecked) {
      try {
        let filterMatch = true;
        if (typeof config.filter === "string") {
          const filterRegex = new RegExp(config.filter);
          filterMatch = filterRegex.test(name);
        } else {
          let isNeedMatch = config.filter.match;
          const filterRegex = new RegExp(config.filter.regex);
          filterMatch = config.filter.match === filterRegex.test(name);
          if (isNeedMatch) {
            if (!filterMatch) {
              isChecked = true;
              violations.push(
                `${kind} name "${name}" Must match filter pattern: ${config.filter.regex}`
              );
            }
          } else {
            if (filterMatch) {
              isChecked = true;
              violations.push(
                `${kind} name "${name}" Must not match filter pattern: ${config.filter.regex}`
              );
            }
          }
        }
      } catch (e) {
        isChecked = true;
        violations.push(`${kind} name "${name}" Invalid filter regex pattern`);
      }
    }

    // 4. 检查前导下划线
    if (config.leadingUnderscore && !isChecked) {
      const hasLeadingUnderscore = /^_/.test(name);
      const hasDoubleLeadingUnderscore = /^__/.test(name);

      switch (config.leadingUnderscore) {
        case "forbid":
          if (hasLeadingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Leading underscore is forbidden`
            );
          }
          break;
        case "require":
          if (!hasLeadingUnderscore || hasDoubleLeadingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Single leading underscore is required`
            );
          }
          break;
        case "requireDouble":
          if (!hasDoubleLeadingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Double leading underscore is required`
            );
          }
          break;
        case "allowDouble":
          if (hasLeadingUnderscore && !hasDoubleLeadingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Only double leading underscore is allowed`
            );
          }
          break;
      }
    }

    // 5. 检查尾随下划线
    if (config.trailingUnderscore && !isChecked) {
      const hasTrailingUnderscore = /_$/.test(name);
      const hasDoubleTrailingUnderscore = /__$/.test(name);

      switch (config.trailingUnderscore) {
        case "forbid":
          if (hasTrailingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Trailing underscore is forbidden`
            );
          }
          break;
        case "require":
          if (!hasTrailingUnderscore || hasDoubleTrailingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Single trailing underscore is required`
            );
          }
          break;
        case "requireDouble":
          if (!hasDoubleTrailingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Double trailing underscore is required`
            );
          }
          break;
        case "allowDouble":
          if (hasTrailingUnderscore && !hasDoubleTrailingUnderscore) {
            isChecked = true;
            violations.push(
              `${kind} name "${name}" Only double trailing underscore is allowed`
            );
          }
          break;
      }
    }

    // 6. 检查前缀
    if (config.prefix?.length && !isChecked) {
      const prefixArr = config.prefix;
      this.checkParamMap.forEach((value, key) => {
        if (key === character && value === line) {
          isChecked = true;
        }
      });
      if (node.flags && !isChecked) {
        const valueName = name;
        let hasPrefix: Boolean = false;

        if (isCopy) {
          this.ruleConfigs.prefixes.forEach((prefix) => {
            if (valueName.startsWith(prefix)) {
              hasPrefix = true;
            }
          });
          prefixArr.forEach((prefix) => {
            if (valueName.startsWith(prefix)) {
              hasPrefix = true;
            }
          });
        } else {
          prefixArr.forEach((prefix) => {
            if (valueName.startsWith(prefix)) {
              hasPrefix = true;
            }
          });
        }
        if (!hasPrefix) {
          isChecked = true;
          violations.push(
            `${kind} name "${name}" "${
              config.prefix
            }" Must start with one of: ${prefixArr.join(", ")}`
          );
        }
        if (config.types && hasPrefix) {
          const [line, character] = this.getNodePosition(node);
          this.checkParamMap.set(node.getText(), line);
        }
      }
    }

    // 7. 检查后缀
    if (config.suffix?.length && !isChecked) {
      const suffixArr = config.suffix;
      this.checkParamMap.forEach((value, key) => {
        if (key === character && value === line) {
          isChecked = true;
        }
      });
      if (node.flags && !isChecked) {
        const valueName = name;
        let hasPrefix: Boolean = false;

        if (isCopy) {
          this.ruleConfigs.suffixes.forEach((prefix) => {
            if (valueName.startsWith(prefix)) {
              hasPrefix = true;
            }
          });
          suffixArr.forEach((suffix) => {
            if (valueName.endsWith(suffix)) {
              hasPrefix = true;
            }
          });
        } else {
          suffixArr.forEach((suffix) => {
            if (valueName.startsWith(suffix)) {
              hasPrefix = true;
            }
          });
        }
        if (!hasPrefix) {
          isChecked = true;
          violations.push(
            `${kind} name "${name}" Must end with one of: ${suffixArr.join(
              ", "
            )}`
          );
        }
        if (config.types && hasPrefix) {
          const [line, character] = this.getNodePosition(node);
          this.checkParamMap.set(node.getText(), line);
        }
      }
    }

    // 最后检查 format，去除前后下划线和前后缀
    if (config.format?.length && !isChecked) {
      let nameWithoutAffixes = name.replace(/^_+|_+$/g, "");
      if (config.prefix && config.prefix.length > 0) {
        const prefixesArr = [...this.ruleConfigs.prefixes, ...config.prefix];
        for (const prefix of prefixesArr) {
          if (nameWithoutAffixes.startsWith(prefix)) {
            nameWithoutAffixes = nameWithoutAffixes.slice(prefix.length);
            break;
          }
        }
      }

      if (config.suffix && config.suffix.length > 0) {
        for (const suffix of this.ruleConfigs.prefixes) {
          if (nameWithoutAffixes.endsWith(suffix)) {
            nameWithoutAffixes = nameWithoutAffixes.slice(0, -suffix.length);
            break;
          }
        }
      }

      const formatRegex: { [key in Format]: RegExp } = {
        camelCase: /^[a-z][a-zA-Z0-9]*$/,
        strictCamelCase: /^[a-z](?:(?:[a-z0-9]*?[A-Z](?<![A-Z]))?[a-z0-9]*)*$/,
        PascalCase: /^[A-Z][a-zA-Z0-9]*$/,
        StrictPascalCase: /^[A-Z](?:(?:[a-z0-9]*?[A-Z](?<![A-Z]))?[a-z0-9]*)*$/,
        snake_case: /^[a-z]+(_[a-z0-9]+)*$/,
        UPPER_CASE: /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/,
      };

      let formatMatches = false;

      if (isCopy) {
        const formats = [...config.format, ...this.formats];
        if (formats.length > this.formats.length) {
          formatMatches = formats.some((format) =>
            formatRegex[format].test(nameWithoutAffixes)
          );
        }
      } else {
        formatMatches = config.format.some((format) =>
          formatRegex[format].test(nameWithoutAffixes)
        );
      }

      if (!formatMatches) {
        violations.push(
          `${kind} name "${name}" must match one of the following formats: ${config.format.join(
            ", "
          )}`
        );
      }
    }

    // 处理违规
    if (violations.length > 0) {
      const position = this.getNodePosition(node);
      const line = position[0];
      const column = position[1];
      const endColumn = node.getEnd();

      violations.forEach((violation) => {
        const description = `${violation}`;
        const severity = this.rule.alert ?? this.metaData.severity;
        const ruleName = "";
        const fileName = this.currentArkFile?.getFilePath() ?? "";

        const location = `${line}%${column}`;
        const fixKey = `${location}%${this.rule.ruleId}`;
        const mergeKey = [fileName, fixKey, description].join("%");

        const defect = new Defects(
          line,
          column,
          endColumn,
          `${description} ${ruleName}`,
          severity,
          this.rule.ruleId,
          mergeKey,
          this.metaData.ruleDocPath,
          true,
          false,
          false
        );

        this.issues.push(new IssueReport(defect,undefined));
        RuleListUtil.push(defect);
      });
    }
  }

  private getNodePosition(node: ts.Node): [number, number] {
    if (!this.currentArkFile) {
        return [0, 0];
    }
    const sourceFile = node.getSourceFile();
    if (!sourceFile) {
        return [0, 0];
    }

    let pos: number;
    
    if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || 
        ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
        pos = node.name?.getStart() ?? node.getStart();
    } else if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) ||
               ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || 
               ts.isSetAccessor(node)) {
        pos = node.name?.getStart() ?? node.getStart();
    } else if (ts.isVariableDeclaration(node)) {
        pos = node.name.getStart();
    } else if (ts.isParameter(node)) {
        pos = ts.isIdentifier(node.name) ? node.name.getStart() : node.getStart();
    } else if (ts.isIdentifier(node)) {
        pos = node.getStart();
    } else {
        pos = node.getStart();
    }

    const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
    return [line + 1, character + 1];
  }
}